#ifndef LOGGER_HPP
#define LOGGER_HPP
#define DEBUG
#include <string>
#include <iostream>
#include <mutex>

using namespace std;

mutex mut;

#if __cplusplus < 201103L
#define Logger_info(args...) { }
#define Logger_warn(args...) { }
#define Logger_error(args...) { }
#define Logger_debug(args...) { }
#else
#define Logger_info Logger_11::info
#define Logger_warn Logger_11::warn
#define Logger_error Logger_11::error
#define Logger_debug Logger_11::debug
#endif

class Logger_11 {
private:
    static bool flag;
public:
    template <typename T>
    static void info(T info);
    template <typename T, typename... Args>
    static void info(T info, Args... args);
    
    template <typename T>
    static void error(T info);
    template <typename T, typename... Args>
    static void error(T info, Args... args);

    template <typename T>
    static void warn(T info);
    template <typename T, typename... Args>
    static void warn(T info, Args... args);
    
    template <typename T>
    static void debug(T info) ;
    template <typename T, typename... Args>
    static void debug(T info, Args... args);
};

bool Logger_11::flag = true;

template <typename T>
void Logger_11::info(T info) {
    mut.lock();
    cout << (Logger_11::flag == false?"":"\033[32;5m[INFO]\033[0m ") << info << endl;
    mut.unlock();
    Logger_11::flag = true;
}
template <typename T, typename... Args>
void Logger_11::info(T info, Args... args) {
    mut.lock();
    cout << (Logger_11::flag == false?"":"\033[32;5m[INFO]\033[0m ") << info << ' ';
    mut.unlock();
    Logger_11::flag = false;
    Logger_11::info(args...);
}
    
template <typename T>
void Logger_11::error(T info) {
    mut.lock();
    cerr << (Logger_11::flag == false?"":"\033[31;5m[Error]\033[0m ") << info << " \033[31;5m[" << strerror(errno)<< "]\033[0m" << endl;
    mut.unlock();
    Logger_11::flag = true;
}
template <typename T, typename... Args>
void Logger_11::error(T info, Args... args) {
    mut.lock();
    cerr << (Logger_11::flag == false?"":"\033[31;5m[Error]\033[0m ") << info << endl;
    mut.unlock();
    Logger_11::flag = true;
}
    
template <typename T>
void Logger_11::warn(T info) {
    mut.lock();
    cerr << (Logger_11::flag == false?"":"\033[33;5m[Warn]\033[0m ") << info << " \033[33;5m[" << strerror(errno)<< "]\033[0m" << endl;
    mut.unlock();
    Logger_11::flag = true;
}
template <typename T, typename... Args>
void Logger_11::warn(T info, Args... args) {
    mut.lock();
    cerr << (Logger_11::flag == false?"":"\033[33;5m[Warn]\033[0m ") << info << endl;
    mut.unlock();
    Logger_11::flag = true;
}

template <typename T>
void Logger_11::debug(T info) {
    #ifdef DEBUG
    mut.lock();
    cout << info << endl;
    mut.unlock();
    #endif
    Logger_11::flag = true;
}
template <typename T, typename... Args>
void Logger_11::debug(T info, Args... args) {
    #ifdef DEBUG
    mut.lock();
    cout << info << endl;
    mut.unlock();
    #endif
    Logger_11::flag = false;
    Logger_11::info(args...);
}

#endif